<template>
  <div id="uploadFile">
    <template v-if="isShowUpload">
      <a-upload
        name="file"
        :multiple="multiple"
        :disabled="disabled"
        :before-upload="beforeUpload"
        :showUploadList="false"
      >
        <div
          v-if="!readOnly"
          :style="{
            cursor: disabled ? 'not-allowed' : 'pointer',
          }"
        >
          <template v-if="isCustomStyle">
            <slot />
          </template>
          <div
            class="upload_file_btn_style"
            :style="{
              'background-color': disabled ? '#b5b5b5' : '#3e71c2',
            }"
            v-else
          >
            <img class="upload_file_icon" src="@/assets/base/upload_icon.png" />
            上传
          </div>
        </div>
        <div style="margin-top: 14px" v-if="!readOnly">
          支持扩展名：{{ fileType }}
        </div>
      </a-upload>
    </template>
    <template v-if="isShowList">
      <transition-group name="list">
        <template v-for="item in newFileList">
          <div class="upload_file_list" :key="item.id">
            <img src="@/assets/base/link_icon.png" class="link_icon" />
            <div
              class="file_info_area"
              :title="item.name"
              @click.stop="downloadFile(item)"
            >
              {{ item.name }}
            </div>
            <img
              @click.stop="deleteFileList(item.id)"
              v-if="!(disabled || readOnly)"
              class="close_icon"
              src="@/assets/base/close_icon.png"
            />
          </div>
        </template>
      </transition-group>
    </template>
  </div>
</template>

<script>
/**
 * @description 默认上传文件组件使用说明方法
 *
 * import UploadFile from "@/components/uploadFile/index.vue";
 * components: { UploadFile },
 * <UploadFile v-model="fileList" :maxLength="maxLength"
 * :disabled="disabled" :customApi="customApi"
 * :isShowList="isShowList" :allowFileType="allowFileType"
 * @customRequest="customRequest" />
 *
 * @param {Array} fileList 文件列表 格式: [path,path] | [{url:fileUrl}]
 * @param {Number} maxLength 上传文件的数量的最大值
 * @param {Boolean} disabled 是否禁用
 * @param {Boolean} readOnly 是否只读
 * @param {Boolean} customApi 是否自定义上传接口
 * @param {Boolean} isShowList 是否显示文件上传列表
 * @param {Array} allowFileType 文件上传类型 格式: [类型名称,类型名称] | [{name:类型名称,rules: 类型校验}]
 *
 * 注：
 * 1. readOnly (是否只读) > disabled (是否禁用)
 * 2. <UploadFile> 在中间添加按钮将会取代默认按钮样式 </UploadFile>
 * 3. customRequest 自定义请求 格式: customRequest(file) {}
 */
import {
  ALLOW_FILE_TYPE,
  ALLOW_IMAGE_TYPE,
  FILE_SIZE,
} from '@/config/global.config';
import { validURL, isArray, isString } from '@/utils/validate';
export default {
  name: 'uploadFile',
  props: {
    // 文件列表
    fileList: {
      type: Array,
      default: () => {
        return [];
      },
    },
    // 上传文件的数量的最大值
    maxLength: {
      type: Number,
      default: 99,
    },
    // 是否支持多选文件
    multiple: {
      type: Boolean,
      default: false,
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false,
    },
    // 是否只读
    readOnly: {
      type: Boolean,
      default: false,
    },
    // 是否自定义上传接口
    customApi: {
      type: Boolean,
      default: false,
    },
    // 是否显示文件上传列表
    isShowList: {
      type: Boolean,
      default: true,
    },
    // 文件上传类型
    allowFileType: {
      type: Array,
      default: () => {
        return [...ALLOW_FILE_TYPE, ...ALLOW_IMAGE_TYPE];
      },
    },
    // 单文件大小限制
    fileSize: {
      type: Number,
      default: () => {
        return FILE_SIZE;
      },
    },
  },
  data() {
    return {
      newFileList: [],
      newAllowFileType: [],
    };
  },
  watch: {
    fileList: {
      handler(newVal) {
        this.newFileList = this.judgeFileList(newVal);
      },
      deep: true,
    },
    allowFileType: {
      handler(newVal) {
        this.newAllowFileType = this.judgeFileList(newVal);
      },
      deep: true,
    },
  },
  model: {
    prop: 'fileList',
    event: 'changeFileList',
  },
  computed: {
    isCustomStyle() {
      return !!this.$slots.default;
    },
    isShowUpload() {
      return this.readOnly || this.maxLength > this.newFileList.length;
    },
    fileType() {
      return this.newAllowFileType.map((item) => `.${item.name}`).join('、');
    },
  },
  mounted() {
    this.newFileList = this.judgeFileList(this.fileList);
    this.newAllowFileType = this.judgeFileRulesList(this.allowFileType);
  },
  methods: {
    beforeUpload(file) {
      const { type, size } = file;
      this.validateFile(this.allowFileType, type, this.fileSize, size) &&
        this.uploadFile(file);
      return false;
    },

    /**
     * @description 对文件类型和文件大小的校验
     * @param {array} allowFileType 允许文件上传的类型
     * @param {string} type 上传文件的类型
     * @param {number} allowFileSize 允许文件上传的大小
     * @param {number} size 上传文件的大小
     */
    validateFile(allowFileType, type, allowFileSize, size) {
      if (allowFileSize < size) {
        this.$toast.fail(`请上传小于${allowFileSize / 1024 / 1024}MB的文件`);
        return false;
      } else if (!new Set(allowFileType.map(item => item.rules)).has(type)) {
        this.$toast.fail('请上传符合格式要求的文件');
        return false;
      } else {
        return true;
      }
    },

    /**
     * @description 上传文件的逻辑
     * @param {file} file
     */
    uploadFile(file) {
      const { name } = file;
      if (this.customApi) {
        this.$emit('customRequest', file);
      } else {
        let formData = new FormData();
        formData.append('file', file);
        formData.append('name', name);
        this.$ajax.uploadFile(formData).then((res) => {
          let list = this.fileList;
          list.push(res);
          this.$emit('changeFileList', list);
        });
      }
    },

    /**
     * @description 将传入的列表进行格式化处理
     * @param {array} list
     * @returns {array}
     */
    judgeFileList(list) {
      if (!isArray(list) || list.length === 0) {
        return [];
      }

      const isObjectWithUrl = (obj) =>
        obj && typeof obj === 'object' && typeof obj.url !== undefined;

      return list
        .map((item, index) => {
          if (isString(item)) {
            return {
              id: -index - 1,
              name: `文件${index + 1}`,
              url: item,
            };
          } else if (isObjectWithUrl(item)) {
            return {
              id: item.id || -index - 1,
              name: item.name || `文件${index + 1}`,
              url: item.url,
            };
          } else {
            return null;
          }
        })
        .filter((item) => item !== null);
    },

    /**
     * @description 将传入的文件规则进行格式化处理
     * @param {array} list
     * @returns {array}
     */
    judgeFileRulesList(list) {
      if (!isArray(list) || list.length === 0) {
        return [];
      }
      const isObjectWithNameAndRules = (obj) =>
        obj &&
        typeof obj === 'object' &&
        typeof obj.name !== undefined &&
        typeof obj.rules !== undefined;

      return list
        .map((item) => {
          if (isString(item)) {
            return [...ALLOW_FILE_TYPE, ...ALLOW_IMAGE_TYPE].find(
              (item2) => item2.name === item
            );
          } else if (isObjectWithNameAndRules(item)) {
            return item;
          } else {
            return null;
          }
        })
        .filter((item) => item !== null);
    },

    /**
     * @description 删除上传列表的文件
     * @param {*} id 需要被删除的id
     */
    deleteFileList(id) {
      let list = this.newFileList.filter((item) => item.id != id);
      this.$emit('changeFileList', list);
      this.$nextTick(() => {
        this.newFileList = list;
      });
    },

    /**
     * @description 下载文件
     * @param {*} file
     */
    downloadFile(file) {
      const { VUE_APP_BASE_API } = process.env;
      const url = VUE_APP_BASE_API + file.url;
      if (validURL(url)) {
        window.open(url);
      } else {
        this.$toast.file('文件或已下架');
      }
    },
  },
};
</script>

<style lang="less" scoped>
.upload_file_btn_style {
  padding: 11px;
  display: flex;
  justify-content: center;
  align-items: center;
  min-width: 85px;
  min-height: 42px;
  text-align: center;
  color: #fff;
  border-radius: 5px;
  box-sizing: border-box;
  letter-spacing: 0.2em;

  .upload_file_icon {
    margin-right: 7px;
    width: 16px;
    height: 15px;
  }
}

.upload_file_list {
  margin-top: 14px;
  padding: 14px 17px;
  display: flex;
  align-items: center;
  font-size: 17px;
  color: #333334;
  background: #f4f6fa;
  border-radius: 5px;
  box-sizing: border-box;

  .file_info_area {
    margin-left: 10px;
    margin-right: 20px;
    flex: 1;
    width: 100%;
    font-weight: 700;
    letter-spacing: 0.1em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
  }

  .link_icon {
    margin-top: 2px;
    width: 20px;
    height: 20px;
  }

  .close_icon {
    width: 14px;
    height: 14px;
    cursor: pointer;
  }
}

.list-enter-active,
.list-leave-active,
.list-move {
  transition: all 0.5s;
}
.list-enter,
.list-leave-to,
.list-move {
  opacity: 0;
  transform: translateY(30px);
}
</style>
